Generated code - Databinding with Windows Forms and ASP.NET, SelfServicing
Preface
Databinding is a .NET feature which can drastically increase productivity when it is implemented correctly.
It is also a technique that works both with single objects and properties as well with collections with objects.
To be able to use databinding with the generated code, both the entity objects and the entity collections are made
databinding aware. This section describes the databinding functionality addressed in the generated code and also the design time functionality
available to you for
windows forms applications and ASP.NET applications.
It is showing a small set of example code how databinding is achieved using collections.
Implemented functionality
This section briefly discusses the implemented functionality in the various classes of the generated code you will use with databinding.
Functionality implemented in Entity classes
From .NET 2.0 onwards, they implemented the interface
INotifyPropertyChanged.
The interface INotifyPropertyChanged serves the same purpose: when a property
changes, it raises an event and bound controls can update themselves if they're interested. This means that
if you bind a property of an entity object to a textbox in your GUI, the textbox is automatically updated when the entity's
field's value is changed using other code than the textbox. Every entity implements IEditableObject.
Typed views and typed lists
Typed View and Typed List objects are generated as classes deriving from DataTable, and because the DataTable already is equipped
with all the databinding functionality necessary, you can bind a Typed View or Typed List without trouble to a datagrid or to a set
of GUI controls.
Entity collections
Entity collections in LLBLGen Pro implement the IListSource interface. This means that bound controls will request from the interface an object they
can bind to. Every entity collection returns its
DefaultView when this request comes. This thus means that when you set a control's DataSource
property to an entity collection instance, the collection won't bind directly to the control though it will bind to the control through its DefaultView.
This also means that if you create your own EntityView instance on a given entity collection, you can bind that EntityView to the control instead, to have
a subset of the data in an entity collection visible in the control. This is similar to how DataTable and DataView work hand in hand. All actions taken on
the data, including creating new entities in a grid for example, are performed on the entity collection. When you're using your own EntityView instance,
be sure to set the
DataChangeAction property of the EntityView instance to the correct value. For details, please see:
Generated code - using the EntityView class for details.
When an entity has 1:m or m:n relations with other entities, it will
expose properties which in their turn will return an entity collection, for example customer has 'Orders' as property, which returns a collection
of entity objects. These collection properties are shown in the DataGrid control when AllowNavigation is set to true and you click open the [+] in
front of the entity row. You can then click on one of the collection returning properties and the grid will be filled with the entity rows of
that collection (through its DefaultView). This way you can browse a complete object model, just by walking relations.
Imagine a form with two datagrids: _master and _detail. Below we'll bind a filled entity collection with customer entities to the upper
datagrid and also to the lower datagrid, and we'll make sure that the lower datagrid will show the order list of the current customer
selected in the upper datagrid, a well known master-detail form. This is achieved with a few lines of code. We'll retrieve a
sorted amount of customer objects having orders which are shipped via the shipped with shipperID 1, filtered using a many-to-many relation.
The customers are sorted on their City field, ascending.
// [C#]
// create the collection of customer entities
ShipperEntity shipper = new ShipperEntity(1);
CustomerCollection customers = new CustomerCollection();
ISortExpression sorter = new SortExpression(CustomerFields.City | SortOperator.Ascending);
customers.GetMultiManyToManyUsingShippers(shipper, 0 , sorter);
// bind it to the master datagrid.
_master.DataSource = customers;
// bind it to the detail as well and make sure orders are shown
_detail.DataSource = customers;
_detail.DataMember = "Orders";
' [VB.NET]
' create the collection of customer entities
Dim shipper As New ShipperEntity(1)
Dim customers As New CustomerCollection()
Dim sorter As ISortExpression = New SortExpression( _
New SortClause(CustomerFields.City, SortOperator.Ascending))
customers.GetMultiManyToManyUsingShippers(shipper, 0 , sorter)
' bind it to the master datagrid.
_master.DataSource = customers
' bind it to the detail as well and make sure orders are shown
_detail.DataSource = customers
_detail.DataMember = "Orders"
This is all that has to be done. The user will now be able to walk through the master grid and when a new row is selected as the active
row, the detail grid will automatically show the Orders' contents of the customer object in the master grid.
You'll also see the lazy loading in action when you walk the customer rows: each time a new customer is selected, its Orders collection
is requested from the database, but it is loaded from the database when the request is actually made, not when the customer is loaded.
EntityView implements all useful properties and methods of IBindingList. Among these features are: sorting in grids, making the EntityView read-only, do not
allow addition, removal of rows.
Table styles and mapping names
When you want to setup table styles in grids, you've to specify the name of the bound object in the MappingName property of the table style. The following rule
is used for this mapping name when you bind an entity collection to a grid:
The list name of an entitycollection bound to a grid is constructed from the following parts: the LLBLGenProEntityName of an instance created by the factory set for
the collection + "Collection". So if the entity factory is set to CustomerEntityFactory, the name is "CustomerEntityCollection".
Design time support in VS.NET 2002/2003
The generated code supports design time databinding out of the box for
all entity collections, typed views and typed lists, in .NET 2.0 and higher.
To use design time databinding, open a form (which can be a webform or a windows forms form)
in design mode and open the toolbox in your IDE (for example Visual Studio.NET). Select the tab for My Controls and select Add/Remove Items....
In the dialog, select Browse... and select the compiled assembly of the generated code. As soon as the IDE (for example Visual Studio.NET) has
investigated which components are available in the selected assembly, it will check and select all of them available (which will be all entity
collections, all typed views and all typed lists). Make sure the selection meets your needs and click OK, which will add all the selected components
to the toolbox.
You can now select one of the components and drag it onto the form, for example the CustomerCollection of the Northwind generated code. The IDE will place the
collection
instance in the component area for the form, which is normally at the bottom of the screen. If you have a grid control located on the form,
you can select its DataSource property and set it to the component, for example the CustomerCollection instance, you've dragged onto the form. When you're
designing a windows forms form, you can also select the DataMember value. For example the CustomerCollection contains CustomerEntity instances, which
contain an EmployeeCollection, OrderCollection etc. which'll show up in the DataMember list.
Using this feature, you can rapidly setup gui's which are bound to classes in the generated code. Because the design time databinding will create an
instance of the class dragged onto the form, for example CustomerCollection, which instance will get customerCollection1 as the default name (which you of
course can change), you only have to add the calls to the GetMulti() method to fill the form with data at runtime.
Design time support in VS.NET 2005
In VS.NET 2005, design time databinding for Windows forms works as described above in the previous section, Design time support in VS.NET 2002/2003,
however some things are made easier for you. VS.NET 2005 will automatically find the entity collections in your project's generated code as well as the
typedlists and typed views. If you don't get the entity collections in your toolbox, please follow the procedure of the previous section to add them to
the toolbox.
In VS.NET 2005, you can still directly bind an entity collection to a grid control, but it's recommended to use a
BindingSource control.
To setup a DataGridView control, not only drag an entity collection onto your form but also a BindingSource control. You then set the
DataGridView's DataSource property to the BindingSource control and the BindingSource' DataSource property to the entity collection dragged onto the form. You
then should see the DataGridView setup with the columns of the entity type contained in the bound entity collection.
ASP.NET got a completely different databinding framework in .NET 2.0 and it requires a different approach. To read more about ASP.NET 2.0+ databinding at design time
and runtime, please see:
Generated code - Databinding with ASP.NET 2.0
Databinding and inheritance
When using an inheritance hierarchy, you typically have subtypes with more fields than the supertypes. As LLBLGen Pro supports polymorphic fetches, it can be
in an entity collection, entities of various types (all from the same inheritance hierarchy) are found. If several of these entity types have fields not found in
their supertypes or siblings in the hierarchy, what will show up in a grid if such a collection is bound to that grid?
LLBLGen Pro will provide to the bound control properties for all fields which are found in the type set for the collection. For example, if you've a hierarchy like
Employee <- Manager <- BoardMember, and you fetch all Manager entities into a ManagerCollection, it can be that one or more of the Manager entities in the
collection are actually of type BoardMember, due to
Polymorphic Fetching. BoardMember entities
have for example a field called CompanyCarId, and Manager entities don't have that field. Binding the fetched ManagerCollection to a grid, only the fields
from the
Manager entity will show up in the grid, as that's the type set for the collection bound, ManagerCollection.
You could argue to show 'null' for fields not in Manager but in subtypes, however what if two types derived from Manager, both having a different set of new fields not
present in the Manager entity? It would be unclear when a user should fill in a field or not, as for each field a column would be created in the case when
all
fields in all types in the collection are shown in the grid. Hence, the display of solely the fields of the type set for the collection.